package com.zhoug.common3.crash;

import android.app.Application;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Looper;
import com.zhoug.logger3.Logger;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;


/**
 * 异常捕获处理器基类,最好自己定义一个类继承它
 * 请使用单例模式
 */
public  class BaseUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = ">>>" + BaseUncaughtExceptionHandler.class.getSimpleName();
    private Application mApplication;
    private String packageName;//包名
    private int versionCode;//版本号
    private String versionName;//版本名
    private Thread.UncaughtExceptionHandler mDefaultHandler;//系统默认的异常处理
    private static final String SEPARATOR = "############我是迷人的分隔符#########";//异常分隔符
    public static final String TAG_UPLOADED = "uploaded_";
    /**
     * 单例
     */
    private static BaseUncaughtExceptionHandler instance;

    protected BaseUncaughtExceptionHandler() {

    }

    public static BaseUncaughtExceptionHandler getInstance(){
        if(instance==null){
            synchronized (BaseUncaughtExceptionHandler.class){
                if(instance==null){
                    instance=new BaseUncaughtExceptionHandler();
                }
            }
        }
        return instance;
    }


    /**
     * 初始化必须调用
     * 在Application中调用
     */
    public void init(Application application) {
        mApplication = application;
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);//设置默认异常处理为自己定义的本类
    }


    /**
     * 发生了未被捕获的异常 回调,把异常保存到手机存储中
     *
     * @param
     * @param
     */
    @Override
    public void uncaughtException(final Thread thread, final Throwable throwable) {
        Logger.e(TAG, "uncaughtException: 发生异常crash了");
        new Thread(() -> {
            new WriteRunnable(throwable).run();
            //主线程发生异常
            if (thread != null && isMainThread(thread) && mDefaultHandler != null) {
                mDefaultHandler.uncaughtException(thread, throwable);
            }

        }).start();

    }

    /**
     * 异常信息写入文件,可以主动调用此方法来记录需要的异常
     * @param throwable
     */
    public void writeExceptionToFile(Throwable throwable) {
        if (isMainThread(Thread.currentThread())) {
            new Thread(new WriteRunnable(throwable)).start();
        } else {
            new WriteRunnable(throwable).run();
        }
    }

    /**
     * 异常写入文件任务
     */
    private class WriteRunnable implements Runnable {
        private Throwable throwable;

        public WriteRunnable(Throwable throwable) {
            this.throwable = throwable;
        }

        @Override
        public void run() {
            synchronized (BaseUncaughtExceptionHandler.this) {
                File todayExceptionFile = getTodayExceptionFile();
                if (todayExceptionFile != null) {
                    Logger.d(TAG, "run:开始写入异常");
                    PrintWriter writer = null;
                    try {
                        FileOutputStream fis = new FileOutputStream(todayExceptionFile, true);
                        writer = new PrintWriter(fis);
                        //开始写入文件
                        //异常发生时间
                        String currentTime = dateFormat("yyyy-MM-dd HH:mm:ss", new Date());
                        writer.println("" + currentTime);
                       /* Server server = ServerManager.getServer();
                        if(server!=null){
                            writer.println("\t(一)服务器信息:");
                            writer.println("服务器名字:" + server.getName());
                            writer.println("API服务器:" + server.getBaseUrl());
                            writer.println("文件服务器:" + server.getFileUrl());
                            writer.println("推流服务器:" + server.getLiveUrl()+"\t\n");
                        }*/
                        writeExceptionInfo(writer);
                        //设备信息
                        writePhoneInfo(writer);
                        //异常信息
                        writer.println("\t(四)异常信息:");
                        throwable.printStackTrace(writer);
                        //分割符
                        writer.println("\t\n" + SEPARATOR + "\t\n");//写入完成
                        writer.flush();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (writer != null) {
                            writer.close();
                            writer = null;
                        }
                    }
                    Logger.d(TAG, "run:结束写入异常");
                } else {
                    Logger.e(TAG, "异常保存文件创建失败:");
                }

            }
        }
    }

    /**
     *在PrintWriter中写入自己需要的通用的异常信息:如服务器信息,用户信息等
     * 需要时请重写此方法
     * @param writer
     */
    public  void writeExceptionInfo(PrintWriter writer){

    }


    /**
     * 设备信息写入文件
     *
     * @param writer 文件输出流
     */
    private void writePhoneInfo(PrintWriter writer) {
        if (packageName == null) {
            packageName = mApplication.getPackageName();
        }
        if (versionName == null) {
            PackageManager packageManager = mApplication.getPackageManager();
            try {
                PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
                if (packageInfo != null) {
                    versionCode = packageInfo.versionCode;
                    versionName = packageInfo.versionName;
                }
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
        String info = "\t(二).App信息:" + "\r\n";
        info += "包名:" + packageName + "\r\n";
        info += "版本名:" + versionName + ",版本号:" + versionCode + "\r\n";
        info += "\t(三).设备信息:" + "\r\n";
        info += "android版本:" + Build.VERSION.RELEASE + ",android API:" + Build.VERSION.SDK_INT + "\r\n";
        info += "制造商:" + Build.MANUFACTURER + "\r\n";
        info += "品牌:" + Build.BRAND + "\r\n";
        info += "设备参数:" + Build.DEVICE + "\r\n";
        info += "型号:" + Build.MODEL + "\r\n";
        info += "版本号:" + Build.DISPLAY + "\r\n";
        info += "CPU架构:" + Build.CPU_ABI + "\r\n";
        info += "硬件名:" + Build.HARDWARE + "\r\n\r\n";

//        Logger.e(TAG, "writePhoneInfo: 手机信息=" + info);
        writer.println(info);

    }


    /**
     * 读取文件中的异常
     *
     * @return
     */
    public String getExceptionFromFile(String path) {
        StringBuffer result = new StringBuffer();
        BufferedReader reader = null;
        try {
            FileInputStream fis = new FileInputStream(path);
            InputStreamReader isr = new InputStreamReader(fis);
            reader = new BufferedReader(isr);
            String line;//一行
            while ((line = reader.readLine()) != null) {
                Logger.d(TAG, "getExceptionFromFile:line=" + line);
                result.append(line);
                result.append("\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                    reader = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result.toString();
    }


    /**
     * 获取异常保存的文件夹:异常文件按日期命名:2020-05-12.txt、2020-05-13.txt
     *
     * @return folder
     */
    private String getExceptionFolder() {
        File root = mApplication.getExternalFilesDir(null);
        if (root == null) {
            root = mApplication.getFilesDir();
        }
        File folder = new File(root, "crash");
        if (!folder.exists()) {
            if (!folder.mkdirs()) {
                Logger.e(TAG, "getExceptionFolder:异常文件夹创建失败");
                return null;
            }
        }
        return folder.getAbsolutePath();
    }

    /**
     * 获取今天的异常保存文件
     *
     * @return
     */
    private File getTodayExceptionFile() {
        String exceptionFolder = getExceptionFolder();
        if (exceptionFolder != null) {
            //异常文件按日期命名
            return new File(exceptionFolder, dateFormat("yyyy_MM_dd", new Date()) + ".txt");
        }
        return null;
    }

    /**
     * 获取所有的异常文件
     *
     * @return
     */
    public List<String> getAllExceptionFiles() {
        String exceptionFolder = getExceptionFolder();
        if (exceptionFolder != null) {
            File folder = new File(exceptionFolder);
            if (folder.exists()) {
                File[] files = folder.listFiles();
                if (files != null && files.length > 0) {
                    List<String> result = new ArrayList<>();
                    for (File file : files) {
                        result.add(file.getAbsolutePath());
                    }
                    return result;
                }
            }
        }
        return null;
    }

    /**
     * 获取需要上传到服务器的异常文件
     *
     * @return
     */
    public List<String> getNeedUploadFiles() {
        String exceptionFolder = getExceptionFolder();
        if (exceptionFolder != null) {
            File folder = new File(exceptionFolder);
            if (folder.exists()) {
                File[] files = folder.listFiles();
                if (files != null && files.length > 0) {
                    List<String> result = new ArrayList<>();
                    for (File file : files) {
                        if (!file.getName().contains(TAG_UPLOADED)) {
                            result.add(file.getAbsolutePath());
                        }
                    }
                    return result;
                }
            }
        }
        return null;
    }


    /**
     * 日期格式化
     *
     * @param format
     * @param date
     * @return
     */
    private String dateFormat(String format, Date date) {
        if (format == null) {
            format = "yyyy_MM_dd";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
        return sdf.format(date);
    }

    /**
     * 标记此文件已经上传到服务器
     *
     * @param path
     */
    public void signUploaded(String path) {
        File oldFile = new File(path);
        String oldName = oldFile.getName();
        if (oldName.contains(TAG_UPLOADED)) {
            Logger.d(TAG, "uploaded:此文件已经上传");
            return;
        }
        String newName = TAG_UPLOADED + oldName;
        File newFile = new File(oldFile.getAbsolutePath().replace(oldName, newName));
        boolean b = oldFile.renameTo(newFile);
    }

    private boolean isMainThread(Thread thread) {
        return "main".equals(thread.getName()) || thread == Looper.getMainLooper().getThread();
    }


}
